import { Injectable, Optional } from '@nestjs/common';
import { eq, and, inArray } from 'drizzle-orm';
import { db, sysUserRole, sysRole, sysRoleMenu, sysMenu } from '@nbase/db';
import { CacheService } from '../common/cache/cache.service';
import { CacheKeyFactory, CACHE_TTL_PRESETS } from '../common/cache/cache-key.factory';

/**
 * RBAC 权限服务
 *
 * 提供角色和权限的查询、验证功能
 * 支持缓存（可选）
 */
@Injectable()
export class RbacService {
  constructor(
    @Optional() private readonly cacheService?: CacheService
  ) {}
  /**
   * 获取用户的所有角色（code）
   * 
   * @param userId 用户 ID
   * @returns 角色 code 数组
   */
  async getUserRoles(userId: string): Promise<string[]> {
    const userRoles = await db
      .select({
        roleCode: sysRole.code,
      })
      .from(sysUserRole)
      .innerJoin(sysRole, eq(sysUserRole.roleId, sysRole.id))
      .where(
        and(
          eq(sysUserRole.userId, userId),
          eq(sysRole.isDeleted, false),
          eq(sysRole.status, 1) // 只查询启用的角色
        )
      );

    return userRoles.map((r) => r.roleCode);
  }

  /**
   * 获取用户的所有权限（permission）
   *
   * @param userId 用户 ID
   * @returns 权限 code 数组，超级管理员返回 ['*']
   */
  async getUserPermissions(userId: string): Promise<string[]> {
    // 1. 查询用户的所有角色 ID 和 code
    const userRoles = await db
      .select({
        roleId: sysUserRole.roleId,
        roleCode: sysRole.code,
      })
      .from(sysUserRole)
      .innerJoin(sysRole, eq(sysUserRole.roleId, sysRole.id))
      .where(
        and(
          eq(sysUserRole.userId, userId),
          eq(sysRole.isDeleted, false),
          eq(sysRole.status, 1)
        )
      );

    if (userRoles.length === 0) {
      return [];
    }

    // 2. 检查是否为超级管理员
    const isSuperAdmin = userRoles.some((r) => r.roleCode === 'super_admin');

    if (isSuperAdmin) {
      return ['*'];
    }

    const roleIds = userRoles.map((r) => r.roleId);

    // 3. 查询这些角色的所有权限
    const permissions = await db
      .select({
        permission: sysMenu.permission,
      })
      .from(sysRoleMenu)
      .innerJoin(sysMenu, eq(sysRoleMenu.menuId, sysMenu.id))
      .where(
        and(
          inArray(sysRoleMenu.roleId, roleIds),
          eq(sysMenu.isDeleted, false),
          eq(sysMenu.visible, true) // 只查询可见的菜单
        )
      );

    // 去重
    return [...new Set(permissions.map((p) => p.permission))];
  }

  /**
   * 获取用户的角色和权限
   *
   * @param userId 用户 ID
   * @returns { roles: string[], permissions: string[] }
   */
  async getUserRolesAndPermissions(userId: string): Promise<{
    roles: string[];
    permissions: string[];
  }> {
    // 使用 CacheService 包装缓存逻辑（如果启用了缓存）
    if (this.cacheService) {
      const cacheKey = CacheKeyFactory.userPermissions(userId);

      return await this.cacheService.wrap(
        cacheKey,
        async () => {
          const [roles, permissions] = await Promise.all([
            this.getUserRoles(userId),
            this.getUserPermissions(userId),
          ]);
          return { roles, permissions };
        },
        CACHE_TTL_PRESETS.LONG  // ✅ 使用预设 TTL（1 小时）
      );
    }

    // 无缓存时直接查询数据库
    const [roles, permissions] = await Promise.all([
      this.getUserRoles(userId),
      this.getUserPermissions(userId),
    ]);

    return { roles, permissions };
  }

  /**
   * 清除用户权限缓存
   * 
   * 在用户角色变更时调用
   */
  async clearUserPermissionsCache(userId: string): Promise<void> {
    if (this.cacheService) {
      await this.cacheService.del(CacheKeyFactory.userPermissions(userId));  // ✅ 使用工厂方法
    }
  }

  /**
   * 检查用户是否有指定角色（任一）
   */
  hasRole(userRoles: string[], requiredRoles: string[]): boolean {
    if (!requiredRoles || requiredRoles.length === 0) {
      return true;
    }

    return requiredRoles.some((role) => userRoles.includes(role));
  }

  /**
   * 检查用户是否有指定权限（任一）
   */
  hasPermission(userPermissions: string[], requiredPermissions: string[]): boolean {
    if (!requiredPermissions || requiredPermissions.length === 0) {
      return true;
    }

    // 超级管理员拥有所有权限
    if (userPermissions.includes('*')) {
      return true;
    }

    return requiredPermissions.some((permission) =>
      userPermissions.includes(permission)
    );
  }

  /**
   * 检查用户是否有所有指定角色
   */
  hasAllRoles(userRoles: string[], requiredRoles: string[]): boolean {
    if (!requiredRoles || requiredRoles.length === 0) {
      return true;
    }

    return requiredRoles.every((role) => userRoles.includes(role));
  }

  /**
   * 检查用户是否有所有指定权限
   */
  hasAllPermissions(userPermissions: string[], requiredPermissions: string[]): boolean {
    if (!requiredPermissions || requiredPermissions.length === 0) {
      return true;
    }

    // 超级管理员拥有所有权限
    if (userPermissions.includes('*')) {
      return true;
    }

    return requiredPermissions.every((permission) =>
      userPermissions.includes(permission)
    );
  }

  /**
   * 获取角色的所有权限
   * 
   * @param roleId 角色 ID
   * @returns 权限 code 数组
   */
  async getRolePermissions(roleId: string): Promise<string[]> {
    const permissions = await db
      .select({
        permission: sysMenu.permission,
      })
      .from(sysRoleMenu)
      .innerJoin(sysMenu, eq(sysRoleMenu.menuId, sysMenu.id))
      .where(
        and(
          eq(sysRoleMenu.roleId, roleId),
          eq(sysMenu.isDeleted, false),
          eq(sysMenu.visible, true)
        )
      );

    return permissions.map((p) => p.permission);
  }

  /**
   * 获取角色的所有菜单（树形结构）
   * 
   * @param roleId 角色 ID
   * @returns 菜单数组
   */
  async getRoleMenus(roleId: string) {
    const menus = await db
      .select({
        id: sysMenu.id,
        name: sysMenu.name,
        permission: sysMenu.permission,
        type: sysMenu.type,
        sort: sysMenu.sort,
        parentId: sysMenu.parentId,
        path: sysMenu.path,
        icon: sysMenu.icon,
        component: sysMenu.component,
        visible: sysMenu.visible,
        keepAlive: sysMenu.keepAlive,
      })
      .from(sysRoleMenu)
      .innerJoin(sysMenu, eq(sysRoleMenu.menuId, sysMenu.id))
      .where(
        and(
          eq(sysRoleMenu.roleId, roleId),
          eq(sysMenu.isDeleted, false)
        )
      )
      .orderBy(sysMenu.sort);

    return menus;
  }
}

